package com.haizhi.crm.sys.message.concurrent;

import com.haizhi.crm.common.log.GLog;
import com.haizhi.crm.common.log.LogFactory;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by wangshan on 2018/5/21.
 */
public class AsyncExecutor<Req, Resp> {

    private static final GLog LOG = LogFactory.getLogger(AsyncExecutor.class);

    public static final int DEFAULT_THREAD_NUM = 5;

    private static final ExecutorService executorService;

    protected Map<String, ActionSupplier<Req, Resp>> suppliers = new LinkedHashMap<>();

    static {
        executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);
    }

    public void join(String taskId, ProcessService<Req, Resp> processService, Req req) {
        this.suppliers.put(taskId, new ActionSupplier(processService, req));
    }

    public Map<String, Resp> actionGet() {
        try {
            // supply
            List<CompletableFuture<Resp>> futures = new ArrayList<>();
            for (ActionSupplier<Req, Resp> actionSupplier : suppliers.values()) {

                CompletableFuture<Resp> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        return actionSupplier.execute();
                    } catch (Exception e) {
                        LOG.error(e);
                    }
                    return null;
                }, executorService);
                futures.add(future);
            }
            // wait for completion
            CompletableFuture<List<Resp>> all = Futures.sequenceCombine(futures);
            List<Resp> results;
            results = all.get();
            return getResultMap(results);
        } catch (Exception e) {
            LOG.error(e);
        }
        return Collections.emptyMap();
    }


    private Map<String, Resp> getResultMap(List<Resp> results) {
        int resultSize = results.size();
        int size = suppliers.size();
        if (resultSize != size) {
            LOG.error("Results size[{0}] not equal to suppliers size[{1}]", resultSize, size);
            return Collections.emptyMap();
        }
        Map<String, Resp> resultMap = new LinkedHashMap<>();
        int index = 0;
        for (String taskId : suppliers.keySet()) {
            resultMap.put(taskId, results.get(index));
            index++;
        }
        return resultMap;
    }

}
